/**
 * Copyright (c) 2011 eelpo.com
 * All rights reserved.
 */
package com.eelpo.common.security;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.SecretKeySpec;

/**
 * 功能描述：公钥和私钥生成器.生成Key,KeyPair,公钥文件,私钥文件,对密钥加解密.
 * <p>
 * 版权所有：广州市艾尔博科技有限公司
 * <p>
 * 未经本公司许可，不得以任何方式复制或使用本程序任何部分
 * 
 * @author <a href="mailto:Brian@eelpo.com">汪洪州</a>
 * @since 1.0
 */
public class KeyBuilder {

	private static KeyBuilder keyBuilder;

	private KeyBuilder() {
	}

	public static KeyBuilder getInstance() {
		return keyBuilder == null ? keyBuilder = new KeyBuilder() : keyBuilder;
	}

	/**
	 * <p>
	 * 方法用途和描述：提供非对称算法公钥和私钥对生成[如：RSA]
	 * </p>
	 * 
	 * @param algorithm
	 *            ：非对称算法，建议使用RSA
	 * @param keySize
	 *            ：非对称算法Key的位长
	 * @return 返回（公钥和私钥对）KeyPair
	 */
	public KeyPair getKeyPair(String algorithm, Integer keySize) {

		try {

			KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
			keyPairGenerator.initialize(keySize.intValue());
			return keyPairGenerator.genKeyPair();

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}

	/**
	 * <p>
	 * 方法用途和描述：提供（对称）密钥生成,使用于对称算法[如：AES,（TripleDES）DESede，Blowfish]
	 * </p>
	 * 
	 * @param algorithm
	 *            ：对称算法，如AES,TripleDES,RC4,Blowfish
	 * @param keySize
	 *            ：对称算法Key的位长
	 * @return 返回（对称）密钥
	 */
	public Key getSymmetryKey(String algorithm, Integer keySize) {

		try {

			KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);
			keyGenerator.init(keySize.intValue());
			return keyGenerator.generateKey();

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}

	/**
	 * <p>
	 * 方法用途和描述：提供基于对称算发的密钥生成
	 * </p>
	 * 
	 * @param algorithm
	 *            ：（PBE|DES）对称算法
	 * @param keySpec
	 *            ：密钥内容的规范
	 * @return 返回特定算法的密钥
	 */
	public Key getSymmetryKey(String algorithm, KeySpec keySpec) {

		try {

			// 通过密钥构造工厂生成密钥
			SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
			return keyFactory.generateSecret(keySpec);

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}

	/**
	 * <p>
	 * 方法用途和描述：提供（对称）密钥生成,使用于对称算法[如：AES,（TripleDES）DESede，Blowfish]
	 * 并对密钥进行基于口令的PBE加密
	 * </P>
	 * 
	 * @param algorithm
	 *            ：对称算法，如AES,TripleDES,RC4,Blowfish
	 * @param password
	 *            ：加密密钥的密码
	 * @param keySize
	 *            ：对称算法Key的位长
	 * @return 返回（对称）密钥字节流数组
	 */
	public byte[] getSymmetryKey(String algorithm, String password, Integer keySize) {

		try {

			KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);
			keyGenerator.init(keySize.intValue());
			return new PBEEncyrptor().encrypt(keyGenerator.generateKey().getEncoded());

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}

	/**
	 * <p>
	 * 方法用途和描述：提供（对称）密钥生成,使用于对称算法[如：AES,（TripleDES）DESede，Blowfish]
	 * 并对密钥进行基于口令的PBE加密后存入文件
	 * </p>
	 * 
	 * @param keyPath
	 *            ：密钥文件路径
	 * @param algorithm
	 *            ：密钥算法
	 * @param password
	 *            ：加密密钥的密码
	 * @param keySize
	 *            ：对称算法Key的位长
	 */
	public void getSymmetryKey(String keyPath, String algorithm, String password, Integer keySize) {

		try {

			OutputStream outputStream = new FileOutputStream(keyPath);
			outputStream.write(getSymmetryKey(algorithm, password, keySize));
			outputStream.close();

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * <p>
	 * 方法用途和描述：从文件中读取已经加密的密钥并进行解密返回
	 * </p>
	 * 
	 * @param keyPath
	 *            ：：密钥文件路径
	 * @param algorithm
	 *            ：密钥算法
	 * @param password
	 *            ：加密密钥的密码
	 * @return 返回密钥对象
	 */
	public Key getSymmetryKey(String keyPath, String algorithm, String password) {

		try {

			InputStream inputStream = new FileInputStream(keyPath);
			ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

			for (int i = inputStream.read(); i != -1; i = inputStream.read())
				outputStream.write(i);
			inputStream.close();

			byte[] keyBytes = new PBEEncyrptor().decrypt(outputStream.toByteArray());
			return new SecretKeySpec(keyBytes, algorithm);

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}

	/**
	 * <p>
	 * 方法用途和描述：创建公钥和私钥对并分别存入文件
	 * </p>
	 * 
	 * @param publicKeyPath
	 *            ：公钥文件路径
	 * @param privateKeyPath
	 *            ：私钥文件路径
	 * @param algorithm
	 *            ：非对称算法
	 * @param keySize
	 *            ：非对称算法Key的位长
	 */
	public void getKeyPair(String publicKeyPath, String privateKeyPath, String algorithm, Integer keySize) {

		try {

			KeyPair keyPair = getKeyPair(algorithm, keySize);

			// Generate PublickKey
			OutputStream outputStream = new FileOutputStream(publicKeyPath);
			outputStream.write(DESEncryptor.encrypt(keyPair.getPublic().getEncoded()));

			// Generate PrivateKey
			outputStream = new FileOutputStream(privateKeyPath);
			outputStream.write(DESEncryptor.encrypt(keyPair.getPrivate().getEncoded()));

			outputStream.close();

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * <p>
	 * 方法用途和描述：把公钥字节流转换成PublicKey对象
	 * </p>
	 * 
	 * @param algorithm
	 *            :公钥算法
	 * @param publicKeyBytes
	 *            ：公钥字节数组
	 * @return 返回PublicKey对象
	 */
	public PublicKey getPublicKey(String algorithm, byte[] publicKeyBytes) {

		try {

			// 公钥内容是遵循X.509标准,创建私钥规范(材料)
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
			KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
			return keyFactory.generatePublic(keySpec);

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}

	/**
	 * <p>
	 * 方法用途和描述：把私钥字节流转换成PrivateKey对象
	 * </p>
	 * 
	 * @param algorithm
	 *            :私钥算法
	 * @param privateKeyBytes
	 *            ：私钥字节数组
	 * @return 返回PrivateKey对象
	 */
	public PrivateKey getPrivateKey(String algorithm, byte[] privateKeyBytes) {

		try {

			// 私钥内容是遵循PKCS#8标准,创建私钥规范(材料)
			PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
			KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
			return keyFactory.generatePrivate(keySpec);

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}

	/**
	 * <p>
	 * 方法用途和描述：将文件中的公钥转换成PublicKey对象
	 * </p>
	 * 
	 * @param publicKeyPath
	 *            ：公钥文件路径
	 * @param algorithm
	 *            ：公钥算法
	 * @return 返回PublicKey对象
	 */
	public PublicKey getPublicKey(String publicKeyPath, String algorithm) {

		try {

			InputStream inputStream = new FileInputStream(publicKeyPath);
			ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

			for (int i = inputStream.read(); i != -1; i = inputStream.read())
				outputStream.write(i);
			inputStream.close();

			return getPublicKey(algorithm, DESEncryptor.decrypt(outputStream.toByteArray()));

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}

	/**
	 * <p>
	 * 方法用途和描述：将文件中的私钥转换成PrivateKey对象
	 * </p>
	 * 
	 * @param privateKeyPath
	 *            ：私钥文件路径
	 * @param algorithm
	 *            ：私钥算法
	 * @return 返回PrivateKey对象
	 */
	public PrivateKey getPrivateKey(String privateKeyPath, String algorithm) {

		try {

			InputStream inputStream = new FileInputStream(privateKeyPath);
			ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

			for (int i = inputStream.read(); i != -1; i = inputStream.read())
				outputStream.write(i);
			inputStream.close();

			return getPrivateKey(algorithm, DESEncryptor.decrypt(outputStream.toByteArray()));

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}
}
